﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.IO;
using dotNails.CodeGen.Util;
using System.Collections;

namespace dotNails.CodeGen.Models {
	public class DbmlData {
		#region Properties
		public string FilePath { get; set; }
		public string Name { get; set; }
		public DataBase DB { get; set; }
		public string ProjectName { get; set; }
		// ManyToManyTables is a dictionary containing all the ManyToMany tables in the dbml. Keyname is Type.Name of the ManyToMany table
		public Dictionary<string, ManyToManyTable> ManyToManyTables = new Dictionary<string, ManyToManyTable>();
		#endregion Properties

		#region Constructors
		public DbmlData() { }

		public DbmlData(DataBase db) {
			this.DB = db;
			InitializeDerivedProperties();
		}

		public DbmlData(string input) {
			this.DB = LoadDataFromXml(input);
			InitializeDerivedProperties();
		}

		public DbmlData(string filePath, string projectName) {
			FilePath = filePath;
			ProjectName = projectName;
			this.DB = LoadDataFromFile(filePath);
			InitializeDerivedProperties();
		}
		#endregion Constructors

		public DataBase LoadDataFromXml(string input) {
			StringReader reader = new StringReader(input);
			XElement databaseXML = XElement.Load(reader);
			return LoadDatabaseFromXElement(databaseXML);
		}

		public void InitializeDerivedProperties() {
			ManyToManyTables.Clear();

			// Find ManyToMany tables and self-referencing tables and modify their Associations accordingly
			foreach (Table table in DB.Tables) {
				// If a table has two associations that are foreign keys, where each assoc.ThisKey == the first two columns' GetPropertyName(), then it's a ManyToMany table
				bool manyToManyReqsMet = false;
				Association firstAssociation = null;
				Association secondAssociation = null;

				// Must have at least two associations and at least two columns to be ManyToMany
				if (table.Type.Associations.Count >= 2 && table.Type.Columns.Count >= 2) {
					// Grab the first two columns in the table.  These are the column names we are trying to match against two foreign-keyed associations for this table.
					//  Convention requires that the many-to-many join columns must be the first two columns in the table.
					List<string> columnNames = new List<string>(); 
					columnNames.Add(table.Type.Columns[0].GetPropertyName());
					columnNames.Add(table.Type.Columns[1].GetPropertyName());

					bool oneMatched = false;
					foreach (Association association in table.Type.Associations) {
						if (association.IsForeignKey && columnNames.Contains(association.ThisKey)) {
							if (oneMatched) {
								// We already matched another foreign-keyed association to the other column, so we know this is a many-to-many table
								manyToManyReqsMet = true;
								secondAssociation = association;
								break;
							}
							else {
								oneMatched = true; 
								columnNames.Remove(association.ThisKey);
								firstAssociation = association;
							}
						}
					}

					if (manyToManyReqsMet) {
						table.IsManyToMany = true;

						ManyToManyTable manyToManyTable = new ManyToManyTable();

						// Get first table from first association.Type
						Table firstTable = DB.Tables.Find(t => t.Type.Name == firstAssociation.Type);
						// Get second table from second association.Type
						Table secondTable = DB.Tables.Find(t => t.Type.Name == secondAssociation.Type);

						// Get the primary key from each table involved in the ManyToMany relationship (we only grab the first primary key).
						string firstTablePrimaryKey = string.Empty;
						string secondTablePrimaryKey = string.Empty;
						foreach (Column column in firstTable.Type.Columns) {
							if (column.IsPrimaryKey) {
								firstTablePrimaryKey = column.GetPropertyName();
								break;
							}
						}
						foreach (Column column in secondTable.Type.Columns) {
							if (column.IsPrimaryKey) {
								secondTablePrimaryKey = column.GetPropertyName();
								break;
							}
						}
						ManyToManyJoin firstAssociationJoin = new ManyToManyJoin {
							ManyToManyTableName = table.Type.Name, // (OrderProduct) The name of the ManyToMany table
							TableName = firstTable.Type.Name, // (Order) Name of this table on ManyToMany
							OtherTableName = secondTable.Type.Name, // (Product) name of the other table on ManyToMany
							PrimaryKey = firstTablePrimaryKey, // (OrderID or ID) this table's PrimaryKey - This table's column.GetPropertyName()
							OtherPrimaryKey = secondTablePrimaryKey, // (ProductID or ID) The other table's PrimaryKey - other table's column.GetPropertyName()
							ManyToManyPropertyName = table.Member, // (OrderProducts) The property name used for the ManyToMany table
							ThisJoinKey = firstAssociation.ThisKey, // (OrderID) The column name used for this table in the ManyToMany table
							OtherJoinKey = secondAssociation.ThisKey, // (ProductID) The column name used for the other table in the ManyToMany table
							OtherPropertyName = StringUtil.Pluralize(secondTable.Type.Name) // (Products) Pluralized form of OtherTableName (stored to boost perf in template)
						};
						manyToManyTable.JoinedTables.Add(firstAssociation.Name, firstAssociationJoin);
						
						ManyToManyJoin secondAssociationJoin = new ManyToManyJoin {
							ManyToManyTableName = table.Type.Name, // The name of the ManyToMany table
							TableName = secondTable.Type.Name, // Name of this table on ManyToMany
							OtherTableName = firstTable.Type.Name, // name of the other table on ManyToMany
							PrimaryKey = secondTablePrimaryKey, // this table's PrimaryKey - This table's column.GetPropertyName()
							OtherPrimaryKey = firstTablePrimaryKey, // The other table's PrimaryKey - other table's column.GetPropertyName()
							ManyToManyPropertyName = table.Member, // The property name used for the ManyToMany table
							ThisJoinKey = secondAssociation.ThisKey, // The column name used for this table in the ManyToMany table
							OtherJoinKey = firstAssociation.ThisKey, // The column name used for the other table in the ManyToMany table
							OtherPropertyName = StringUtil.Pluralize(firstTable.Type.Name) // Pluralized form of OtherTableName (stored to boost perf in template)
						};
						manyToManyTable.JoinedTables.Add(secondAssociation.Name, secondAssociationJoin);

						ManyToManyTables.Add(table.Type.Name, manyToManyTable);
					}
					//}
				}

				// Check for any self-referencing joins
				if (!manyToManyReqsMet) {
					if (table.Type.Associations.Count >= 1) {
						foreach (Association association in table.Type.Associations) {
							// Check if this table's association references itself (self-referencing)
							if (association.Type == table.Type.Name) {
								association.IsSelfReferencing = true;
								if (association.IsForeignKey) {
									// Lop off "ID" if it's present in the column name (e.g. if the column is called "ParentID", the member will be called "Parent")
									association.Member = RemoveIDFromEndOfString(association.ThisKey);
									// Change the OtherMember property on the association
									association.OtherMember = "Child" + StringUtil.Pluralize(association.Type);
								}
								else {
									association.Member = "Child" + StringUtil.Pluralize(association.Type); // (e.g. if table is "Navigation", this member should be "ChildNavigations")
									// Change the OtherMember property on the association
									association.OtherMember = RemoveIDFromEndOfString(association.OtherKey);
								}
							}
						}
					}

				
				}
			}

			// Identify ManyToMany associations (endpoints) in all the non ManyToMany tables
			foreach (Table table in DB.Tables) {
				if (!table.IsManyToMany) {
					// Check for a ManyToMany association
					foreach (Association association in table.Type.Associations) {
						// If a table has an association that is not a foreign key and a Type equal to the name (Type.Name) of a ManyToMany table, and that ManyToManyTable has a ManyToManyJoin with 
						//  the same association.Name, then it is a ManyToMany association
						if (!association.IsForeignKey && ManyToManyTables.ContainsKey(association.Type) && ManyToManyTables[association.Type].JoinedTables.ContainsKey(association.Name)) {
							association.IsManyToManyAssociation = true;
							// Grab the ManyToManyJoin info to help template performance
							association.ManyToManyJoin = ManyToManyTables[association.Type].JoinedTables[association.Name];
						}
					}
				}
			}
			
		}

		private static string RemoveIDFromEndOfString(string input) {
			string member = input;
			if (member.ToLower().EndsWith("id")) {
				member = member.Substring(0, member.Length - 2);
			}
			return member;
		}

		public DataBase LoadDataFromFile(string dbmlFilePath) {
			DataBase database;
			Name = Path.GetFileNameWithoutExtension(dbmlFilePath);
			
			// Check for empty file - will cause exception
			FileInfo info = new FileInfo(dbmlFilePath);
			if (info.Length <= 3) {
				// Empty file returns length of three (probably a header)
				database = new DataBase();
			}
			else {
				XElement databaseXML = XElement.Load(dbmlFilePath);
				database = LoadDatabaseFromXElement(databaseXML);
			}

			return database;
		}

		private DataBase LoadDatabaseFromXElement(XElement databaseXML) {
			XNamespace ns = (string)databaseXML.Attribute("xmlns");	// "http://schemas.microsoft.com/linqtosql/dbml/2007";

			DataBase database = new DataBase {
				Name = (string)databaseXML.Attribute("Name"),
				Class = (string)databaseXML.Attribute("Class"),
				XmlNamespace = (string)databaseXML.Attribute("xmlns"),
				Connection = (from c in databaseXML.Elements(ns + "Connection")
							  select new Connection() {
								  ConnectionString = (string)c.Attribute("ConnectionString"),
								  Mode = (string)c.Attribute("Mode"),
								  Provider = (string)c.Attribute("Provider"),
								  SettingsObjectName = (string)c.Attribute("SettingsObjectName"),
								  SettingsPropertyName = (string)c.Attribute("SettingsPropertyName")
							  }).SingleOrDefault(),
				Tables = (from t in databaseXML.Elements(ns + "Table")
						  select new Table() {
							  Member = (string)t.Attribute("Member"),
							  Name = (string)t.Attribute("Name"),
							  Type = (from type in t.Elements(ns + "Type")
									  select new Type() {
										  Name = (string)type.Attribute("Name"),
										  Columns = (from col in type.Elements(ns + "Column")
													 select new Column() {
														 CanBeNull = (bool)col.Attribute("CanBeNull"),
														 DbType = (string)col.Attribute("DbType"),
														 // This is how to handle attributes that might not be present.  Without this, you'll get a "Value cannot be null" error when they're missing.
														 IsDbGenerated = col.Attributes("IsDbGenerated").Any() ? (bool)col.Attribute("IsDbGenerated") : false,
														 IsPrimaryKey = col.Attributes("IsPrimaryKey").Any() ? (bool)col.Attribute("IsPrimaryKey") : false,
														 Member = col.Attributes("Member").Any() ? (string)col.Attribute("Member") : string.Empty,
														 UpdateCheck = col.Attributes("UpdateCheck").Any() ? (string)col.Attribute("UpdateCheck") : string.Empty,
														 Name = (string)col.Attribute("Name"),
														 Type = (string)col.Attribute("Type"),
													 }).ToList(),
										  Associations = (from a in type.Elements(ns + "Association")
														  select new Association() {
															  Member = (string)a.Attribute("Member"),
															  Storage = a.Attributes("Storage").Any() ? (string)a.Attribute("Storage") : string.Empty,
															  Name = (string)a.Attribute("Name"),
															  ThisKey = a.Attributes("ThisKey").Any() ? (string)a.Attribute("ThisKey") : string.Empty,
															  OtherKey = (string)a.Attribute("OtherKey"),
															  Type = (string)a.Attribute("Type"),
															  IsForeignKey = a.Attributes("IsForeignKey").Any() ? (bool)a.Attribute("IsForeignKey") : false,
															  OtherMember = (string)type.Attribute("Name") // Default to the name of the containing table (Table.Type.Name to be specific)
														  }).ToList()
									  }).Single()
						  }).ToList(),
				Functions = (from f in databaseXML.Elements(ns + "Function")
							 select new Function() {
								Name = (string)f.Attribute("Name"),
								Method = (string)f.Attribute("Method"),
								Parameters = (from p in f.Elements(ns + "Parameter")
											   select new Parameter() {
												   Name = (string)p.Attribute("Name"),
												   ParameterName = p.Attributes("Parameter").Any() ? (string)p.Attribute("Parameter") : string.Empty,
												   Type = (string)p.Attribute("Type"),
												   DbType = (string)p.Attribute("DbType"),
												   Direction = p.Attributes("Direction").Any() ? (string)p.Attribute("Direction") : string.Empty,
											   }).ToList(),
								ElementType = f.Elements(ns + "ElementType").Any() ? // ElementType might not be present
												(from e in f.Elements(ns + "ElementType")
												select new ElementType() {
													Name = (string)e.Attribute("Name"),
													IdRef = e.Attributes("IdRef").Any() ? (string)e.Attribute("IdRef") : string.Empty,
													Columns = (from col in e.Elements(ns + "Column")
															   select new Column() {
																   Name = (string)col.Attribute("Name"),
																   Type = (string)col.Attribute("Type"), 
																   DbType = (string)col.Attribute("DbType"),
																   CanBeNull = (bool)col.Attribute("CanBeNull")
															   }).ToList()
												}).SingleOrDefault() : null, // no ElementType
								Return =		f.Elements(ns + "Return").Any() ? 
												(from e in f.Elements(ns + "Return")
												 select new Return() {
													 Type = (string)e.Attribute("Type")
												 }).SingleOrDefault() : null
							 }).ToList()

			};

			return database;
		}	

		public XElement SerializeToXml() {
			try {
				XNamespace ns = XNamespace.Get(DB.XmlNamespace);

				// Get rid of all underscores in column names
				//foreach (Table table in DB.Tables

				// Create new table objects from the table selections
				XElement xml = new XElement(ns + "Database",
									new XAttribute("Name", DB.Name),
									new XAttribute("Class", DB.Class),
									new XAttribute("xmlns", DB.XmlNamespace),
									new XElement(ns + "Connection",
										new XAttribute("Mode", DB.Connection.Mode),
										new XAttribute("ConnectionString", DB.Connection.ConnectionString),
										new XAttribute("SettingsObjectName", DB.Connection.SettingsObjectName),
										new XAttribute("SettingsPropertyName", DB.Connection.SettingsPropertyName),
										new XAttribute("Provider", DB.Connection.Provider)
									),
									from table in DB.Tables
									select new XElement(ns + "Table",
										new XAttribute("Name", table.Name),
										DbmlUtil.HasMemberProperty(table) ? new XAttribute("Member", table.Member) : null, // this handles writing no xml here if this value is not set
										new XElement(ns + "Type",
											new XAttribute("Name", table.Type.Name),
											from column in table.Type.Columns
											select new XElement(ns + "Column",
												new XAttribute("Name", column.Name),
												DbmlUtil.HasMemberProperty(column) ? new XAttribute("Member", column.Member) : null, // this handles writing no xml here if this value is not set
												//!string.IsNullOrEmpty(column.Member) ? new XAttribute("Member", column.Member) : null, // this handles writing no xml here if this value is not set
												new XAttribute("Storage", column.Storage),
												new XAttribute("Type", column.Type),
												new XAttribute("DbType", column.DbType),
												new XAttribute("IsPrimaryKey", column.IsPrimaryKey),
												new XAttribute("IsDbGenerated", column.IsDbGenerated),
												new XAttribute("CanBeNull", column.CanBeNull),
												!string.IsNullOrEmpty(column.UpdateCheck) ? new XAttribute("UpdateCheck", column.UpdateCheck) : null
											),
											from association in table.Type.Associations
											select new XElement(ns + "Association",
												new XAttribute("Name", association.Name),
												new XAttribute("Member", association.Member),
												!string.IsNullOrEmpty(association.Storage) ? new XAttribute("Storage", association.Storage) : null, // this handles writing no xml here if this value is not set
												new XAttribute("ThisKey", association.ThisKey ?? string.Empty),
												new XAttribute("OtherKey", association.OtherKey),
												new XAttribute("Type", association.Type),
												new XAttribute("IsForeignKey", association.IsForeignKey)
											)
										)
									),
									from function in DB.Functions
									select new XElement(ns + "Function",
										new XAttribute("Name", function.Name),
										new XAttribute("Method", function.Method),
										from parameter in function.Parameters
										select new XElement(ns + "Parameter",
											new XAttribute("Name", parameter.Name),
											!string.IsNullOrEmpty(parameter.ParameterName) ? new XAttribute("Parameter", parameter.ParameterName) : null,
											new XAttribute("Type", parameter.Type),
											new XAttribute("DbType", parameter.DbType),
											!string.IsNullOrEmpty(parameter.Direction) ? new XAttribute("Direction", parameter.Direction) : null
										),
										(function.ElementType != null) ? // ElementType might not be present
										new XElement(ns + "ElementType",
											!string.IsNullOrEmpty(function.ElementType.IdRef) ? new XAttribute("IdRef", function.ElementType.IdRef) : null, // this handles writing no xml here if this value is not set
											!string.IsNullOrEmpty(function.ElementType.Name) ? new XAttribute("Name", function.ElementType.Name) : null,
											from column in function.ElementType.Columns
											select new XElement(ns + "Column",
												new XAttribute("Name", column.Name),
												new XAttribute("Type", column.Type),
												new XAttribute("DbType", column.DbType),
												new XAttribute("CanBeNull", column.CanBeNull)
											)
										) : null, // no ElementType
										(function.Return != null) ? // Return might not be present
										new XElement(ns + "Return",
											new XAttribute("Type", function.Return.Type)
										) : null // no Return
									)
								);

				return xml;
			}
			catch (Exception ex) {
				throw ex;
			}
		}

	}

	[Serializable]
	public class DataBase {
		private const string DefaultLinqXmlNamespace = "http://schemas.microsoft.com/linqtosql/dbml/2007";

		public string Name { get; set; }
		public string Class { get; set; }
		private string _xmlNamespace;
		public string XmlNamespace {
			get {
				if (string.IsNullOrEmpty(_xmlNamespace)) {
					_xmlNamespace = DefaultLinqXmlNamespace;
				}

				return _xmlNamespace;
			}
			set { _xmlNamespace = value; }
		}

		public Connection Connection { get; set; }
		private List<Table> _tables = new List<Table>();
		public List<Table> Tables {
			get { return _tables; }
			set { _tables = value; }
		}
		private List<DbView> _views = new List<DbView>();
		public List<DbView> Views {
			get { return _views; }
			set { _views = value; }
		}
		private List<Function> _functions = new List<Function>();
		public List<Function> Functions {
			get { return _functions; }
			set { _functions = value; }
		}
	}

	[Serializable]
	public class Connection {
		private const string DefaultMode = "WebSettings";
		private const string DefaultProvider = "System.Data.SqlClient";
		private const string DefaultSettingsObjectName = "System.Configuration.ConfigurationManager.ConnectionStrings";

		private string _mode;
		public string Mode {
			get {
				if (string.IsNullOrEmpty(_mode)) {
					_mode = DefaultMode;
				}

				return _mode;
			}
			set { _mode = value; }
		}
		public string ConnectionString { get; set; }
		private string _settingsObjectName;
		public string SettingsObjectName {
			get {
				if (string.IsNullOrEmpty(_settingsObjectName)) {
					_settingsObjectName = DefaultSettingsObjectName;
				}

				return _settingsObjectName;
			}
			set { _settingsObjectName = value; }
		}
		public string SettingsPropertyName { get; set; }
		private string _provider;
		public string Provider {
			get {
				if (string.IsNullOrEmpty(_provider)) {
					_provider = DefaultProvider;
				}

				return _provider;
			}
			set { _provider = value; }
		}
		
	}

	[Serializable]
	public class Table {
		public string Name { get; set; }
		public string Member { get; set; }
		public Type Type { get; set; }
		private List<TableRelationship> _tableRelationships = new List<TableRelationship>();
		public List<TableRelationship> TableRelationships { 
			get { return _tableRelationships; }
			set { _tableRelationships = value; }
		}

		// Derived properties
		public bool IsManyToMany { get; set; }
	}

	[Serializable]
	public class DbView {
		public string Name { get; set; }
	}

	[Serializable]
	public class Function {
		public string Name { get; set; }
		public string Method { get; set; }
		private List<Parameter> _parameters = new List<Parameter>();
		public List<Parameter> Parameters {
			get { return _parameters; }
			set { _parameters = value; }
		}
		public ElementType ElementType { get; set; }
		public Return Return { get; set; }
	}

	[Serializable]
	public class Parameter {
		public string Name { get; set; }
		public string ParameterName { get; set; }
		public string Type { get; set; }
		public string DbType { get; set; }
		public string Direction { get; set; }
		public bool IsOutputParameter {
			get {
				bool result = false;
				if (Direction == "InOut") {
					result = true;
				}
				return result;
			}
		}
		public string GetParameterName() {
			string parameterName;

			if (!string.IsNullOrEmpty(this.ParameterName)) {
				parameterName = this.ParameterName;
			}
			else {
				parameterName = this.Name;
			}

			return parameterName;
		}
	}

	[Serializable]
	public class ElementType {
		public string Name { get; set; }
		public string IdRef { get; set; }
		private List<Column> _columns = new List<Column>();
		public List<Column> Columns {
			get { return _columns; }
			set { _columns = value; }
		}
	}

	[Serializable]
	public class Return {
		public string Type { get; set; }
	}

	/// <summary>
	/// This class is used internally to store relevant information from SMO in order to properly
	/// construct the Association objects needed by the dbml.  It's also a lot clearer and easier
	/// to understand than the SMO fields.
	/// </summary>
	[Serializable]
	public class TableRelationship {
		public bool Paired { get; set; }
		public bool IsReferencingTable { get; set; }
		public string ForeignKeyName { get; set; }
		public string TableName { get; set; }
		public string ReferencingTable { get; set; }
		public string ReferencedTableSchema { get; set; }
		public string ReferencingTableSchema { get; set; }
		public string ReferencedTable { get; set; }
		public string ReferencingColumn { get; set; }
		public string ReferencedColumn { get; set; }
	}

	[Serializable]
	public class Type {
		public string Name { get; set; }
		public string Id { get; set; }
		public List<Column> Columns { get; set; }
		public List<Association> Associations { get; set; }
	}

	[Serializable]
	public class Column {
		public string Name { get; set; }
		public string Member { get; set; }
		public string Storage {
			get {
				return DbmlUtil.GetCSharpVariableName(this);
			}
		}
		private string _updateCheck = string.Empty;
		public string UpdateCheck {
			get {
				string updateCheck = _updateCheck;
				if (string.IsNullOrEmpty(updateCheck)) {
					if (IsPrimaryKey) {
						updateCheck = string.Empty;
					}
					else {
						updateCheck = "Never"; // dotNails convention for primarily disconnected usage
					}
				}
				
				return updateCheck;
			}
			set { _updateCheck = value; } 
		}
		public string Type { get; set; }
		public string DbType { get; set; }
		public bool IsPrimaryKey { get; set; }
		public bool IsDbGenerated { get; set; }
		public bool CanBeNull { get; set; }

		public string GetPropertyName() {
			string propertyName;

			if (!string.IsNullOrEmpty(this.Member)) {
				propertyName = this.Member;
			}
			else {
				propertyName = this.Name;
			}

			return propertyName;
		}
	}

	[Serializable]
	public class Association {
		public string Name { get; set; }
		public string Member { get; set; }
		public string Storage { get; set; }
		private string _thisKey = string.Empty;
		public string ThisKey {
			get {
				if (string.IsNullOrEmpty(_thisKey)) {
					_thisKey = OtherKey;
				}
				return _thisKey;
			}
			set { _thisKey = value; }
		}
		private string _otherKey = string.Empty;
		public string OtherKey {
			get {
				if (string.IsNullOrEmpty(_otherKey)) {
					_otherKey = ThisKey;
				}
				return _otherKey;
			}
			set { _otherKey = value; }
		}
		public string Type { get; set; }
		public bool IsForeignKey { get; set; }

		#region Derived Properties
		// These are not written to or read from the dbml xml directly, they are derived via convention from values above
		private bool _isSelfReferencing = false;
		public bool IsSelfReferencing {
			get { return _isSelfReferencing; }
			set { _isSelfReferencing = value; } 
		}
		public string OtherMember { get; set; }
		public bool IsManyToManyAssociation { get; set; }
		// Contains all the data concerning the ManyToManyJoin (other table name, primary key column names, etc)
		public ManyToManyJoin ManyToManyJoin { get; set; }
		#endregion Derived Properties
	}

	[Serializable]
	public class ManyToManyTable {
		// Should contain two entries - one for each ManyToMany association.  Keyname is the association.Name)
		public Dictionary<string, ManyToManyJoin> JoinedTables = new Dictionary<string, ManyToManyJoin>();
	}
	[Serializable]
	public struct ManyToManyJoin {
		public string ManyToManyTableName; // The name of the ManyToMany table (table.Type.Name)
		public string TableName; // This table's name (table.Type.Name)
		public string OtherTableName; // The name of the other table in the ManyToMany relationship (table.Type.Name)
		public string PrimaryKey; // This table's primary key column (column.GetProperyName())
		public string OtherPrimaryKey; // The other table's primary key column (column.GetProperyName())
		public string ManyToManyPropertyName; // The property name used for the ManyToMany table
		public string ThisJoinKey; // The column name used for the this table in the ManyToMany table
		public string OtherJoinKey; // The column name used for the other table in the ManyToMany table
		public string OtherPropertyName; // Pluralized form of OtherTableName (stored to boost perf in template)
	}
	
}